package com.moralesce.gt.model;

import org.apache.commons.lang3.StringUtils;

/**
 * One of 52 of cards that are used to play card games.
 * 
 * <p>
 * A card is comprised of one rank and one suit, with 13 ranks and 4 suits there
 * are 52 unique playing cards.
 * </p>
 * 
 * <p>
 * A card can be created in one of two ways. <br/>
 * 
 * The first method uses the Rank and Suit enumerators: <br/>
 * 
 * <code>
 * // Create the four of hearts.
 * Card card = new Card(Rank.FOUR, Suit.HEART);
 * </code><br/>
 * 
 * The second method uses rank and suit values. <br/>
 * 
 * <code>
 * // Create the four of hearts.
 * Card card = new Card("4H");
 * </code><br/>
 * 
 * The rank and suit values are case insensitive: <br/>
 * 
 * <code>
 * // Create the four of hearts. <br/>
 * Card card = new Card("4h"); <br/>
 * 
 * // Create the king of spades. <br/>
 * Card card = new Card("kS"); <br/>
 * </code>
 * 
 * </p>
 * 
 * @see Rank
 * @see Suit
 * 
 * @author Brandon Grenier
 */
public class Card implements Comparable<Card> {

	private final Suit suit;
	private final Rank rank;

	/**
	 * Creates a new card with the given card value.
	 * 
	 * The card key is a case insensitive string exactly two characters in
	 * length, the first character being the rank key (one of 2, 3, 4, 5, 6, 7,
	 * 8, 9, 10, J, Q, K, A) and the second character being the suit key (one of
	 * S, C, H, D).
	 * 
	 * @param value The card value.
	 */
	public Card(String value) {
		if (value == null || value.length() != 2) {
			throw new IllegalArgumentException("A card must be created with a 2 character value");
		}
		this.rank = Rank.forValue(String.valueOf(value.charAt(0)));
		this.suit = Suit.forValue(String.valueOf(value.charAt(1)));
	}

	/**
	 * Creates a new cards with the specified rank and suit.
	 * 
	 * @param rank The rank.
	 * @param suit The suit.
	 */
	public Card(Rank rank, Suit suit) {
		if (suit == null || rank == null) {
			throw new NullPointerException();
		}
		this.suit = suit;
		this.rank = rank;
	}

	/**
	 * Returns the card id.
	 * 
	 * @return The card id.
	 */
	public Integer getId() {
		return suit.id() * rank.getId();
	}

	/**
	 * Returns the card suit.
	 * 
	 * @return The card suit.
	 */
	public Suit getSuit() {
		return suit;
	}

	/**
	 * Returns the card rank.
	 * 
	 * @return The card rank.
	 */
	public Rank getRank() {
		return rank;
	}

	/**
	 * Returns the card value.
	 * 
	 * The card value is a 2 character string, the first character being the
	 * card's rank value and the second character being the card's suit value.
	 * 
	 * For example, '4S' is the card value for the four of spades, 'QH' is the
	 * card value for the queen of hearts.
	 * 
	 * @return The card value.
	 */
	public String getValue() {
		return rank.getValue() + suit.value();
	}

	public int hashCode() {
		return this.getId().hashCode();
	}

	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof Card)) {
			return false;
		}
		Card card = (Card) obj;
		return card.getId().equals(this.getId());
	}

	public String toString() {
		return "name=>" + StringUtils.capitalize(rank.name().toLowerCase()) + " of " + suit.name().toLowerCase() + "s, id=>" + getId();
	}

	public int compareTo(Card card) {
		return card.getRank().getOrder() - this.getRank().getOrder();
	}

}
